/*

  This file is part of Kato Javascript Framework (http://kato.googlecode.com/)

  Copyright (c) 2007, Filip Dreger, Jan Urbański

  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution.
      * Neither the name of Kato Javascript Framework nor the names
        of its contributors may be used to endorse or promote products
        derived from this software without specific prior written
        permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

nag = true
nagme = function(msg){if (nag) nag = confirm(msg)}

Kato = {}

Kato.Active = {}
Kato.Request = {}
Kato.Templates = {}
Kato.Cookies = {}
Kato.Config = {}
Kato.Uploads = {}
Kato.Config.Markers = 'php/logs/markers'
Kato.Config.BaseUrl = '/kato'
Kato.Config.LogoutUrl = '?logout=1'
Kato.Config.KeyColumnRegex = new RegExp("^id_");
Kato.Config.Flash = false;
Kato.Flash = {}
Kato.Utils = {}

DumperMaxDepth = 3

Kato.Error = function(msg) {
	if(msg.indexOf('cannot connect')==0){
		if(Kato.Config.LogoutUrl) window.location.href=Kato.Config.LogoutUrl
		return
	}
	alert(msg)
}
Kato.Log = function(){}
Kato.Translations = {
	new_item:		'wyczyść',
	save_new:		'zapisz nowy',
	clear:			'wyczyść',
	edit:			'zapisz',
	add:			'dodaj',
	save:			'nagraj',
	remove:			'usuń',
	next_page:		'następna',
	prev_page:		'poprzednia',
	page:			'strona',
	confirm_remove:	'operacja usunięcia jest nieodwracalna. Czy na pewno kontyunować?',
	confirm_new_item:'wszystkie zmiany wprowadzone od ostatniego zapisu zostaną stracone. Czy na pewno kontynuować?',

	error_not_null:		'wartość jest wymagana',
	error_integer:		'wymagana jest liczba całkowita',
	error_numeric:		'wymagana jest liczba',
	error_timestamp:	'wymagane jest podanie czasu w formacie RRRR-MM-DD GG:MM',
	error_date:		'wymagane jest podanie czasu w formacie RRRR-MM-DD',
	error_time:		'wymagane jest podanie godziny w formacie GG:MM(:SS)'
}

Kato.Cookies.options = {}
Kato.Cookies.options.expires = '360000000000'
Kato.Cookies.options.path = ''
Kato.Cookies.options.domain = ''
Kato.Cookies.options.secure = ''
Kato.Cookies.keys = $A()

Kato.Cookies.put = function(key, value) {
	str = key + '=' + Object.toJSON(value)
	if (Kato.Cookies.options.expires) {
		date = new Date()
		date.setTime(date.getTime() + parseInt(Kato.Cookies.options.expires))
		str += '; expires=' + date.toGMTString()
	}
	if (Kato.Cookies.options.path) {
		str += '; path=' + Kato.Cookies.options.path
	}
	if (Kato.Cookies.options.domain) {
		str += '; domain=' + Kato.Cookies.options.domain
	}
	if (Kato.Cookies.options.secure) {
		str += '; secure'
	}
	Kato.Cookies.keys.push(key)
	document.cookie = str
}

Kato.Cookies.get = function(key) {
	value = document.cookie.match(key + '=([^;]*)(;|$)')
	return value ? value[1].evalJSON() : undefined
}

Kato.Cookies.remove = function(key) {
	date = new Date()
	date.setTime(date.getTime() - 3600)
	str = key + '=; expires=' + date.toGMTString()
	if (Kato.Cookies.options.path) {
		str += '; path=' + Kato.Cookies.options.path
	}
	if (Kato.Cookies.options.domain) {
		str += '; domain=' + Kato.Cookies.options.domain
	}
	if (Kato.Cookies.options.secure) {
		str += '; secure'
	}
	document.cookie = str
}

Kato.Cookies.clear = function() {
	Kato.Cookies.keys.each(function(key) {
		Kato.Cookies.remove(key)
	})
}

Kato.Utils.date_from_string = function(date) {
		return new Date(parseInt(date.substr(0,4)),parseInt(date.substr(5,2).replace(/^0/,''))-1,parseInt(date.substr(8,2).replace(/^0/,'')))
}

Kato.Utils.string_from_timestamp = function (date){
	var hours = date.getHours()
	var minutes = date.getMinutes()
	if (minutes<10) minutes = '0' + minutes
	if (hours<10) hours = '0' + hours
	return Kato.Utils.string_from_date(date)
			+ ' '
			+ hours
			+ ':'
			+ minutes
}

Kato.Utils.timestamp_from_string = function(date){
	try {
		var year = parseInt(date.substr(0,4))
		var month = parseInt(date.substr(5,2).replace(/^0/,''))-1
		var day = parseInt(date.substr(8,2).replace(/^0/,''))
		var hours = parseInt(date.substr(11,2).replace(/^0/,''))		
		var minutes = parseInt(date.substr(14,2).replace(/^0/,''))
		if (!hours) hours = 0;
		if (!minutes) minutes = 0;
	} catch (e) {
		return null
	}
	return new Date(year,month,day, hours, minutes, 0)	
}

Kato.Utils.this_month_ge = function() {
	var mydate = new Date();
	mydate.setDate(1)
	return Kato.Utils.string_from_date(mydate)	
}

Kato.Utils.this_month_lt = function() {
	var mydate = new Date()
	mydate.setMonth(mydate.getMonth()+1,1)
	return Kato.Utils.string_from_date(mydate)		
}

Kato.Utils.string_from_date = function(date) {
		return ''
			+ date.getFullYear()
			+ '-'
			+ ((date.getMonth()<9) ? '0' : '')
			+ (date.getMonth()+1)
			+ '-'
			+ ((date.getDate()<10) ? '0' : '')
			+ date.getDate()			
}

Kato.Utils.idle = {
	element: undefined,	
	init:function(element){
		if(element==undefined){
			element = document.createElement('div');
			element.id = 'kato_flash_idle_container'
			document.body.insertBefore(element,document.body.firstChild)
			element.style.position = 'absolute';
			
		}
		this.element = element
		if(!Kato.Config.Flash){
			element.innerHTML='Wczytywanie...'
			element.style.fontSize='20px'
			element.style.backgroundColor='white'
		} else {
			element.style.width = '64px';
			var so = new SWFObject(Kato.Config.BaseUrl + '/flash/idle.swf', "kato_flash_idle", "32", "32", "9", "#FFF");
	  		so.addParam("wmode", "transparent");
	  		so.write(this.element.id)
		}
	},

	busy:function(){
		//if(!Kato.Config.Flash)return;
		if(this.element==undefined){
			this.init()
		}
//		document.body.style.cursor='wait'
		this.element.style.display = ''
		//alert('busy')
	},
	
	idle:function(){
		//if(!Kato.Config.Flash)return;
		if(this.element==undefined){
			this.init()
		}
//		document.body.style.cursor=''
		this.element.style.display = 'none'
		//alert('idle')
	}
}


Kato.Utils.MP3 = {
	init:function(url){
		if(Kato.Flash.mp3) return;
		if(!SWFObject)Kato.Error('MP3 functionality in Kato requires the SWFObject Javascript library (free, easy to find, go get it)')
		var so = new SWFObject(Kato.Config.BaseUrl + '/flash/mp3.swf', "kato_flash_thingy", "0", "0", "9", "#F00");
		so.addParam("allowScriptAccess", "always");
		var element = document.createElement('div');
		element.id = 'kato_flash_thingy_container'
		document.body.insertBefore(element,document.body.firstChild)
		so.write("kato_flash_thingy_container");
		Kato.Flash.mp3 = document.getElementById('kato_flash_thingy')
	},

	load:function(url){
		Kato.Flash.mp3.load(url)
	},

	play:function(){
		Kato.Flash.mp3.play()
	},
	stop:function(){
		Kato.Flash.mp3.stop()
	},
	set_volume:function(vol){
		Kato.Flash.mp3.set_volume(vol)
	}
	
}

Kato.Updater = function(){
	for (name in Kato.Active){
		if(Kato.Active[name].refresh){
			Kato.Active[name].refresh()
		}
	}
	//setTimeout('Kato.Updater()',10000)
}

Kato.Months = [
	'Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień',
	'Październik','Listopad','Grudzień'
]

Kato.Weekdays = [
	'Pon','Wt','Śr','Czw','Pt','So','Nie'
]

/*Kato.Months = [
	'January','February','March','April','May','June','July','August','September','October','November','December'
]*/

//Kato.Log = DumperAlert

Kato.Regex = {
	escapeRegex: new RegExp(
		'(\\' + [ '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '/' ].join('|\\') + ')',
		'g'
	),
	
	escape: function(val) {
		return val.replace(Kato.Regex.escapeRegex, '\\$1')
	}
}

Kato.Translate = function(text){
	if(Kato.Translations[text]){
		text = Kato.Translations[text]
	} else {
		text = text.replace(/_/g,' ')
	}
	return text
}

Kato.Store = {}

Kato.Helpers = {
	inject_dropdown:function(id_where,id_parent,bind_col,bind_row){
		var params = {}
		params.id = ($(id_where).id?$(id_where).id:'') + id_parent + 'Kdropdown' + bind_col + '_' + bind_row
		params.model = Kato.PrimaryKeys[Kato.Active[id_parent].columns[bind_col].column]
		if(!Kato.PrimaryKeys[Kato.Active[id_parent].columns[bind_col].column]){
			Kato.Error('wrong view for key ' + Kato.Active[id_parent].columns[bind_col].column)
		}
		params.type = 'Kato.Dropdown'
		params.cell_binding= "'" + id_parent + "'," + bind_col + "," + bind_row
		Kato.BuildFromScratch(params,id_where)
		Kato.Active[params.id].value = Kato.Active[id_parent].rows[bind_row][bind_col]	
		Kato.Active[params.id].render()
		$(id_where).onclick = ''
		Kato.Active[params.id].change_handlers.push(
			function(){
				Kato.Active[params.id].model.remove_listener(params.id)
				Kato.Active[id_parent].send_data()
				delete Kato.Active[params.id]
			}
		)
	},
	
	
	inject_combo:function(id_where,id_parent,bind_col,bind_row){
		var params = {}
		params.id = ($(id_where).id?$(id_where).id:'') + id_parent + 'Kcombo' + bind_col + '_' + bind_row
		params.model = Kato.PrimaryKeys[Kato.Active[id_parent].columns[bind_col].column]
		if(!Kato.PrimaryKeys[Kato.Active[id_parent].columns[bind_col].column]){
			Kato.Error('wrong view for key ' + Kato.Active[id_parent].columns[bind_col].column)
		}
		params.type = 'Kato.Combo'
		params.cell_binding= "'" + id_parent + "'," + bind_col + "," + bind_row
		Kato.BuildFromScratch(params,id_where)
		Kato.Active[params.id].value = Kato.Active[id_parent].rows[bind_row][bind_col]	
		Kato.Active[params.id].render()
		$(id_where).onclick = ''
		Kato.Active[params.id].change_handlers.push(
			function(){
				Kato.Active[params.id].model.remove_listener(params.id)
				Kato.Active[id_parent].send_data()
				delete Kato.Active[params.id]
			}
		)
	}
	
}

Kato.Resize = function(element,mouse_x){
	element = $(element)
	element.original_width = Element.getWidth(element)
	element.original_pointerX = mouse_x
	Kato.Store.resize_observer = function(event){
		document.body.style.cursor = 'e-resize'
		element.style.width = element.original_width + Event.pointerX(event) - element.original_pointerX
	}
	Kato.Store.stop_resizing = function(event){
		document.body.style.cursor = ''
		Event.stopObserving(document.body,'mousemove',Kato.Store.resize_observer)
		Event.stopObserving(document.body,'mouseup',Kato.Store.stop_resizing)
	}
	Event.observe(document.body,'mousemove',Kato.Store.resize_observer)
	Event.observe(document.body,'mouseup',Kato.Store.stop_resizing)	
}


Kato.GetModel = function(viewname){
	if (!Kato.Active[viewname]){
		Kato.Active[viewname] = new Kato.Model(viewname)
	}
	return Kato.Active[viewname]
}

Kato.Upload = function(upload_id){
	var upload = Kato.Uploads[upload_id]
	upload.bind_to.model.filename_column = upload.column_index
	upload.bind_to.set_cell(upload.row_index, upload.column_index, upload.filename)
	upload.bind_to.send_data()
}

Kato.Extract = function(element, attribute, container, default_value) {
	if (default_value === undefined) default_value = ''
	var content = element.hasAttribute(attribute) ? element.getAttribute(attribute) : default_value;
	var code
	if (container.constructor == Object) {
		code = '({' + content + '})'
	}
	else if (container.constructor == Array) {
		code = '([' + content + '])'
	}
	else if (container.constructor == String) {
		code = '(\'' + content + '\')'
	}
	else if (container.constructor == Boolean) {
		code = '(' + content + ')'
	}
	//var code = (container.constructor == Object ? ('({' + content + '})') : ('([' + content + '])'))
	return eval(code)
}

Kato.Renderers = {
	get_renderer_show_column:function(column_to_show){
		return function(row,index,id_row_index){
			return '<td>' + row[column_to_show] + '</td>'
		}
	},
	get_renderer_dropdown_cell:function(column_with_pk){
		return function(row,index,id,row_index){
			return Kato.Renderers.build_dropdown_cell.bind(this)(row,index,id,row_index,column_with_pk)
		}
	},
	get_renderer_combo_cell:function(column_with_pk){
		return function(row,index,id,row_index){
			return Kato.Renderers.build_combo_cell.bind(this)(row,index,id,row_index,column_with_pk)
		}
	},
	
	build_download_cell:function(row,index,id,row_index){
		var o = '';
		o += '<td><a href="' + this.model.get_download_url(row[0],row[index]) + '">' + row[index] + '</a></td>'
		return o
	},
	
	build_editable_cell:function(row,index,id,row_index){
			var o = '';
			o += "<td onclick='this.innerHTML=Kato.Renderers.build_bound_input(" + row.toJSON() + "," + index + ",\"" + id+ "\", " + row_index + "); this.childNodes[0].focus()'>";
			o += ((row[index]!=='' && row[index]!=null)?row[index]:'&nbsp;')
			o += "</td>\n"
			return o
	},
	
	build_dropdown_cell:function(row,index,id,row_index,column_with_pk){
			if(column_with_pk===undefined)column_with_pk=index
			if(typeof(column_with_pk)!='number') column_with_pk = this.model.to_index(column_with_pk)
			var o = ''
			o += "<td onclick='Kato.Helpers.inject_dropdown(this,\"" + id + "\", " + column_with_pk + "," + row_index + ")'>"
			o += row[index] + "</td>"
			return o
	},
	
	build_combo_cell:function(row,index,id,row_index,column_with_pk){
			if(column_with_pk===undefined)column_with_pk=index
			if(typeof(column_with_pk)!='number') column_with_pk = this.model.to_index(column_with_pk)
			var o = ''
			o += "<td onclick='Kato.Helpers.inject_combo(this,\"" + id + "\", " + column_with_pk + "," + row_index + ")'>"
			o += row[index] + "</td>"
			return o
	},

	build_booldropdown_cell:function(row,index,id,row_index){
		var val = row[index]
		if(!(val==null || val=='f'))val='t'
		if (val=='t') {
			tsel = 'selected'
			fsel = ''
		} else {
			tsel = ''
			fsel = 'selected'
		}
		var o = ''
		o += '<td>'
		o += '<select onchange="val = this.options[this.selectedIndex].value; Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',val)"><option ' + tsel + ' value="t">tak</option><option ' + fsel + ' value="f">nie</option></select>'
		o += '</td>'
		return o
	},
	
	build_date_cell:function(row,index,id,row_index){
		Kato.Utils.string_from_date(new Date())
		var o = '';
		var val = Kato.Active[id].rows[row_index][index]
		val = ((val!=null)?val:'')
		o += '<input style="width: 12em" onkeyup="Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',this.value)" class="text" type="text" value="' + val + '" />'
		o += "<input type='button' class='kato_button' value='dziś' onclick='this.previousSibling.value=Kato.Utils.string_from_date(new Date()); this.previousSibling.onkeyup()'>";
		return o
	},
	
	build_timestamp_cell:function(row,index,id,row_index){
		Kato.Utils.string_from_date(new Date())
		var o = '';
		var val = Kato.Active[id].rows[row_index][index]
		val = ((val!=null)?val:'')
		o += '<input style="width: 12em" onkeyup="Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',this.value)" class="text" type="text" value="' + val + '" />'
		o += "<input type='button' class='kato_button' value='teraz' onclick='this.previousSibling.value=Kato.Utils.string_from_date(new Date()) + \" \" + new Date().getHours() + \":\" + new Date().getMinutes(); this.previousSibling.onkeyup()'>";
		return o
	},

	build_date_form_cell:function(row,index,id,row_index){
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += Kato.Renderers.build_date_cell(row,index,id,row_index)
		o += '</tr>'
		return o		
	},	
	
	build_timestamp_form_cell:function(row,index,id,row_index){
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += Kato.Renderers.build_timestamp_cell(row,index,id,row_index)
		o += '</tr>'
		return o		
	},	
	
	build_checkbox_cell:function(row,index,id,row_index){
		var val = row[index]
		if(!(val==null || val=='f' || val==''))val='t'
		var checked = (val=='t')?"checked='checked'":""
		var o = ''
		o += '<td>'
		o += '<input type="checkbox" onchange="Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',this.checked?\'t\':\'f\')" class="text" ' + checked + '>'
		o += '</td>'
		return o
	},



    build_booldropdown_cell:function(row,index,id,row_index){
	var val = row[index]
	if(!(val==null || val=='f'))val='t'
	if (val=='t') {
	    tsel = 'selected'
	    fsel = ''
	} else {
	    tsel = ''
	    fsel = 'selected'
	}
	var o = ''
	o += '<td>'
	o += '<select onchange="val = this.options[this.selectedIndex].value; Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',val)"><option ' + tsel + ' value="t">tak</option><option ' + fsel + ' value="f">nie</option></select>'
	o += '</td>'
	return o
    },
    
	build_booldropdown_form_cell:function(row,index,id,row_index){
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += Kato.Renderers.build_booldropdown_cell(row,index,id,row_index)
		o += '</tr>'
		return o		
	},
	
	build_checkbox_form_cell:function(row,index,id,row_index){
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += Kato.Renderers.build_checkbox_cell(row,index,id,row_index)
		o += '</tr>'
		return o		
	},
	
	file_upload_form_field:function(row,index,id,row_index){
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += Kato.Renderers.build_file_upload_cell.bind(this)(row,index,id,row_index)
		o += '</tr>'
		return o
	},
	
	
	build_file_upload_cell:function(row,index,id,row_index){
		if(row[index]) return '<td>' + row[index] + '</td>'
		var o=''
		o += Kato.Renderers.build_file_upload.bind(this)(row,index,id,row_index)
		return '<td>' + o + '</td>'
	},
	
	build_file_upload:function(row,index,id,row_index){
		var o = ''
		var base_name = this.id + 'KuploadK' + row[0]
		Kato.Uploads[base_name] = {bind_to:this, 'row_index':row_index, 'column_index':index}
		o += "<form target='" + base_name + "' action='" + Kato.Config.BaseUrl + "/php/' method='post' enctype='multipart/form-data'>"
		o += "<input type='file' name='file' onchange='this.parentNode.submit()' />"
		o += "</form><iframe style='display: none' name='" + base_name + "'></iframe>"
		return o
	},
	
	
	
	build_bound_input:function(row,index,id,row_index){
		var o = '';
		var val = Kato.Active[id].rows[row_index][index]
		val = ((val!=null)?val:'')
		o += '<input onclick="Event.stop(event)" onkeyup="if(event.keyCode==Event.KEY_RETURN){Kato.Active.' + id + '.send_data()};Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',this.value)" class="text" type="text" value="' + val + '">'
		return o
	},

	build_textarea_form_field:function(row,index,id,row_index){
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += Kato.Renderers.build_textarea_cell.bind(this)(row,index,id,row_index)
		o += '</tr>'
		return o
	},
	
	build_textarea_cell:function(row,index,id,row_index){
		var o = '';
		var val = Kato.Active[id].rows[row_index][index]
		val = ((val!=null)?val:'')
		o += '<textarea onclick="Event.stop(event)" onkeyup="if(event.keyCode==Event.KEY_RETURN){Kato.Active.' + id + '.send_data()};Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',this.value)" class="text" type="text">' + val + '</textarea>'
		return o
	},

	build_order_switcher:function(row, index, id, row_index) {
		// dając buttony zamiast obrazków można zaoszczędzić ~2s czasu ładowania strony...
		var o = '<td style="text-align: center">';
		o += '<img class="orderby_arrow" src="' + Kato.Config.BaseUrl + '/web/img/up-arrow.png" style="margin: 0px"'
		//o += '<input type="button" class"kato_button" value="w górę" '
		if (row_index != 0) {
			o += 'onclick="var tmp = Kato.Active.' + id + '.rows[' + (row_index - 1) + '][' + index + ']; Kato.Active.' + id + '.set_cell(' + (row_index - 1) + ',' + index + ',\'' + row[index] + '\'); Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',tmp); Kato.Active.' + id + '.send_data()"'
		}
		else {
			o += 'onclick="Event.stop(event)"'
		}
		o += '>'
		o += '<img class="orderby_arrow" src="' + Kato.Config.BaseUrl + '/web/img/down-arrow.png" style="margin: 0px"'
		//o += '<input type="button" class"kato_button" value="w dół" '
		if (row_index != (Kato.Active[id].rows.length - 1)) {
			o += 'onclick="var tmp = Kato.Active.' + id + '.rows[' + (row_index + 1) + '][' + index + ']; Kato.Active.' + id + '.set_cell(' + (row_index + 1) + ',' + index + ',\'' + row[index] + '\'); Kato.Active.' + id + '.set_cell(' + row_index + ',' + index + ',tmp); Kato.Active.' + id + '.send_data()"'

		}
		else {
			o += 'onclick="Event.stop(event)"'
		}
		o += '>'
		o += '</td>'
		return o
	}
}

Kato.Mutators = {
	strip:function(val) {
		if(typeof(val)=='string')
			return val.strip()
		return val
	},

	strip_whitespace:function(val) {
		return val.replace(/\s*/g, '')
	},

	commas_to_points:function(val) {
		return val.replace(/,/g, '.')
	},

	parse_timestamp:function(val) {
		return val //FIXME potrzebny potężny parser
	},

	parse_date:function(val) {
		return val //FIXME potrzebny potężny parser
	},
	
	set_default:function(def) {
		return function(val) { return val == '' ? def : val }
	}
}

Kato.Validators = {
	not_null:function(val) {
		return val != '' ? null : Kato.Translate('error_not_null')
	},

	integer:function(val) {
		return val.match(/^(\s*[+-]?[0-9]+\s*)?$/) ? null : Kato.Translate('error_integer')
	},

	numeric:function(val) {
		return val.match(/^(\s*[+-]?[0-9]*\.?[0-9]+\s*)?$/) ? null : Kato.Translate('error_numeric')
	},
	
	// poniżej musi przechodzić: "2007-11-12 12:49:26.678529"
	timestamp:function(val) {
		return val.match(/^(\d\d\d\d-\d\d-\d\d\s*(\d{1,2}(:\d{1,2}(:\d\d(.\d*)?)?)?)?)?$/) ? null : Kato.Translate('error_timestamp')
		//return val.match(/^(\s*([1-9]|([0-1][0-9])|(2[0-4])):[0-5]?[0-9]?(:[0-5]?[0-9])?\s*)?$/) ? null : Kato.Translate('error_timestamp')
		//return val == '' ? null : !isNaN(Date.parse(val)) ? null : Kato.Translate('error_timestamp')
		return null //FIXME potrzebny potężny regex
	},

	date:function(val) {
		// wyłapuje większość błędów, lepsze niż nic...
		return val.match(/^(\d\d\d\d-(\d)?\d-(\d)?\d)?$/) ? null : Kato.Translate('error_date')
		return null //FIXME potrzebny potężny regex
	},

	time:function(val) {
		return val.match(/^(\d\d:\d\d(:\d\d)?)?$/) ? null : Kato.Translate('error_time')
	},

	text:function(val, row) {
		return null
	}
}

Kato.Active.dummy_model = {
	select:function(){
		return {
			data:[],
			columns:[],
			rows:[]
		}
		
	},
	col_name_to_index:function(){},
	count:function(){},
	refresh:function(){},
	add_listener:function(){}
}


Kato.Model = Class.create();
Kato.Model.prototype = Object.extend ({},{

	after_select:		false,

	data:			new Array(),
	operations:		new Array(),
	listeners:		new Array(),
	pending_validators:	undefined,
	pending_mutators:	undefined,
	validators:		undefined,
	mutators:		undefined,
	current_filters:	undefined,
	filename_column:	undefined,
	view:				undefined,
	refresh_view:		undefined,
	
	initialize:function(view){
		this.view = view
		this.refresh_view = view
		this.state = 'empty'
		this.current_filters = new Object()
		this.flush_mode = 'always'
		this.listeners = new Array()
		this.operations = new Array()
		this.pending_validators = new Array()
		this.pending_mutators = new Array()
		this.validators = new Array()
		this.mutators = new Array()
	},

	initialize_validators:function(){
		this.validators[0] = []
		for (var i = 1; i < this.data.columns.length; i++) {
			this.validators[i] = []
			if (this.data.columns[i]['not null'] && !this.data.columns[i]['has default']) {
				this.validators[i].push(Kato.Validators.not_null)
			}
			switch (this.data.columns[i]['type']) {

			case 'int4':
				this.validators[i].push(Kato.Validators.integer)
				break

			case 'numeric':
				this.validators[i].push(Kato.Validators.numeric)
				break

			case 'text':
			case 'varchar':
				this.validators[i].push(Kato.Validators.text)
				break

			case 'date':
				this.validators[i].push(Kato.Validators.date)
				break

			case 'timestamp':
			case 'timestamptz':
				this.validators[i].push(Kato.Validators.timestamp)
				break

			case 'time':
				this.validators[i].push(Kato.Validators.time)
				break

			}
		}
		this.pending_validators.each((function(elem) {
			this.validators[this.to_index(elem[0])].push(elem[1])
		}).bind(this))
	},

	initialize_mutators:function(){
		this.mutators[0] = []
		for (var i = 1; i < this.data.columns.length; i++) {
			this.mutators[i] = []
			this.mutators[i].push(Kato.Mutators.strip)
			switch (this.data.columns[i]['type']) {

			case 'numeric':
				this.mutators[i].push(Kato.Mutators.commas_to_points)
				/* fallthrough */
			case 'int4':
				this.mutators[i].push(Kato.Mutators.strip_whitespace)
				break

			case 'timestamp':
			case 'timestamptz':
				this.mutators[i].push(Kato.Mutators.parse_timestamp)
				break

			case 'date':
				this.mutators[i].push(Kato.Mutators.parse_date)
				break
			}
		}
		this.pending_mutators.each((function(elem) {
			this.mutators[this.to_index(elem[0])].push(elem[1])
		}).bind(this))
	},

	add_listener:function(listener){
		this.listeners.push(listener)
	},

	remove_listener:function(listener){
		for (var v=0; v < this.listeners.length; v++){
			if(this.listeners[v]==listener){
				this.listeners.splice(v,1);
				return;
			}
		}
	},

	add_validator:function(index, validator){
		if (this.after_select) {
			this.validators[this.to_index(index)].push(validator)
		}
		else {
			this.pending_validators.push([index, validator])
		}
	},

	add_mutator:function(index, mutator){
		if (this.after_select) {
			this.mutators[this.to_index(index)].push(mutator)
		}
		else {
			this.pending_mutators.push([index, mutator])
		}
	},

	refresh:function(){
		var url = Kato.Config.BaseUrl + '/' + Kato.Config.Markers + '/' + this.refresh_view
		var request = new Ajax.Request(
			url,
			{
				asynchronous: false
			}
		)

		if(request.transport.status==200 && request.transport.responseText!=this.data.head.revision){
			this.reload()
			this.data.head.revision = request.transport.responseText
			return true;
			//alert([this.data.head.revision,request.transport.responseText])
		} else {
			//alert([this.data.head.revision,request.transport.responseText])
			return false;
		}
	},
	
	reload:function(){
			this.operations = []
			this.state = 'empty'
			this.notify_listeners()
	},

	apply_mutators:function(row) {
		var mutated = []
		$A(row).each(function (elem) {mutated.push(elem)})
		for (var i = 0; i < this.data.columns.length; i++) {
			for (var j = 0; j < this.mutators[i].length; j++) {
				mutated[i] = this.mutators[i][j].bind(this)(mutated[i] === null ? '' : mutated[i], mutated)
			}
		}
		return mutated
	},

	validate:function(row) {
		var retval = []
		var mutated = this.apply_mutators(row)
		for (var i = 0; i < this.data.columns.length; i++) {
			for (var j = 0; j < this.validators[i].length; j++) {
				var response = this.validators[i][j].bind(this)(mutated[i] === null ? '' : mutated[i], mutated)
				if (response !== null) {
					retval.push({ column:i, message:response })
					break
				}
			}
		}
		return retval
	},

	get_download_url:function(id,filename){
		return Kato.Config.BaseUrl + '/php/?view=' + this.view + '&download=' + id + '&filename=' + filename 
	},
	
	
	
	flush_operations:function(){
			var url = Kato.Config.BaseUrl + '/php/?view=' + this.view;
			if(this.filename_column != undefined){
				url += '&filename_column=' + this.filename_column
			}
			Kato.Utils.idle.busy();
			var request = new Ajax.Request(
				url,
				{
					method:'post',
					parameters: {operations: this.operations.toJSON()},
					asynchronous: false
				}
			)
			
			if(request.transport.responseText){
				var data = eval('('+request.transport.responseText+')');
				if (data.error){
					Kato.Error(data.error)
					this.operations = []
					throw 'invalid data'
				}
			}
			this.operations = []
			this.state = 'empty'
			this.filename_column = undefined
			this.notify_listeners()
			Kato.Utils.idle.idle();
	},

	select:function(params, server_params){
		if(params===undefined) params = {}
		if(server_params===undefined) server_params = []
		var local = {}
		var server = {}

		$H(params).each(function(pair) {
			if ($A(server_params).member(pair[0])) {
				server[pair[0]] = pair[1]
			}
			else {
				local[pair[0]]= pair[1]
			}
		})

		var i = 0
		if (this.state != 'full' ||
		    $H(this.current_filters).size() != $H(server).size() ||
		    $H(this.current_filters).any(function (pair) {
			    return !$H(server).keys().member(pair[0]) ||
				    server[pair[0]] != pair[1]
		    })) {
			//nagme('muszę wykonać requesta\n  view = ' + this.view + '\n  server_params = ' + server_params + '\n  state = ' + this.state + '\n  current_filters = ' + Dumper(this.current_filters) + '\n  server = ' + Dumper(server) + '\n  local = ' + Dumper(local) + '\n  params = ' + Dumper(params))
			this.request_data(server)
			this.current_filters = server
			this.state = 'full'
		}

		var result = new Array()
		var offset = 0
		var limit = 999999 // :-)
		var length = this.data.rows.length
		var fetched = 0;

		var where_text = '({where:function(row){'
		for(var param in local){
			var search = param.match(/^search_(.+)$/);
			if (search){
				if(local[param] === undefined) continue
				if (local[param] === null || local[param] == '') {
					where_text += "return false;"
				}
				else {
					var search_field_index = this.col_name_to_index(search[1])
					if(typeof(local[param])=='string'){
						where_text += "if (row["+search_field_index+"]!='"+local[param]+"') return false;";						
					} else {
						where_text += "if (row["+search_field_index+"]!="+local[param]+") return false;";
					}
				}
			}
			var regexp = param.match(/^regexp_(.+)$/);
			if (regexp && local[param]!==''){
				var search_field_index = this.col_name_to_index(regexp[1])
				where_text += "if (row["+search_field_index+"]===null)return false; if (!row["+search_field_index+"].match(/" + Kato.Regex.escape(local[param]).replace(/ +/g,'.*') + "/i)) return false;";
			}
			var isnull = param.match(/^isnull_(.+)$/);
			if (isnull && local[param]!=''){
				var search_field_index = this.col_name_to_index(isnull[1])
				where_text += 'if (row["'+search_field_index+'"]'+(local[param] == true ? '!==' : '===')+'null)return false;'
			}
			
			var search_field_index = param.match(/^[^_]*_(.+)$/) ? this.col_name_to_index(param.match(/^[^_]*_(.+)$/)[1]) : null;
			
			/* TODO: w tej chwili lt, gt, le i ge porownuja tylko alfabetycznie, a powinny takze numerycznie */
			
			var lt = param.match(/^lt_(.+)$/);
			if (lt && local[param]!=='' && local[param]!==''){
				var quoted_param = local[param].match(/^[\d.]$/) ? local[param] : "'" + local[param] + "'"
				where_text += "if (row["+search_field_index+"] >= " + quoted_param + ") return false;";
			}
			var ge = param.match(/^ge_(.+)$/);
			if (ge && local[param]!=='' && local[param]!==''){
				var quoted_param = local[param].match(/^[\d.]$/) ? local[param] : "'" + local[param] + "'"
				where_text += "if (row["+search_field_index+"] < " + quoted_param + ") return false;";
			}
			var gt = param.match(/^gt_(.+)$/);
			if (gt && local[param]!=='' && local[param]!==''){
				var quoted_param = local[param].match(/^[\d.]$/) ? local[param] : "'" + local[param] + "'"
				where_text += "if (row["+search_field_index+"] <= " + quoted_param + ") return false;";
			}
			var le = param.match(/^le_(.+)$/);
			if (le && local[param]!=='' && local[param]!==''){
				var quoted_param = local[param].match(/^[\d.]$/) ? local[param] : "'" + local[param] + "'"
				where_text += "if (row["+search_field_index+"] > " + quoted_param + ") return false;";
			}
		}
		where_text += 'return true;}})'
		Kato.Log(where_text,'filter')
		var where = eval(where_text)

		if(local.offset) offset = local.offset
		if(local.limit) limit = local.limit

		//FIXME: depending on whether these is a limit/offset, should sorting be done before or after filtering?

		for(var param in local){
			var search = param.match(/^orderby_(.+)$/);
			if (search) {
				var ordermod = 1;
				if (local[param] == 'desc') {
					ordermod = -1;
				}
				var field_index = this.col_name_to_index(search[1]);
				var sort_function;
				switch (this.data.columns[field_index]['type']) {
					case 'int4':
						sort_function = function(a, b) {
							a = parseInt(a);
							b = parseInt(b);
							if (isNaN(a) && isNaN(b))
								return 0;
							if (a > b || isNaN(a))
								return 1;
							if (a < b || isNaN(b))
								return -1;
							return 0;
		                                };
						break;
					case 'numeric':
						sort_function = function(a, b) {
							a = parseFloat(a);
							b = parseFloat(b);
							if (isNaN(a) && isNaN(b))
								return 0;
							if (a > b || isNaN(a))
								return 1;
							if (a < b || isNaN(b))
								return -1;
							return 0;
		                                };
						break;
					default:
						sort_function = function(a, b) {
							if (a === null && b === null)
								return 0;
							if (a > b || a === null)
								return 1;
							if (a < b || b === null)
								return -1;
							return 0;
		                                };
				}
				this.data.rows.sort(function(a, b) { return sort_function(a[field_index], b[field_index]) * ordermod; });
			}
		}

		var cursor = 0;

		while (cursor<length && fetched < limit){
			var row = this.data.rows[cursor]
			if (where.where(row)){
				if(offset--<=0){
					result.push(row.slice())
					fetched++
				}
			}
			cursor++
		}

		return {
			'columns':this.data.columns,
			'rows':result,
			'head':this.data.head
		}
	},

	request_data:function(params) {
		var saved_revision;
		if(this.view != this.view_refresh && this.data.head) saved_revision = this.data.head.revision
		var url = Kato.Config.BaseUrl + '/php/?view=' + this.view
		$H(params).each(function(pair) {
			url += '&search[' + pair[0] + ']=' + encodeURIComponent(pair[1])
		})
		Kato.Utils.idle.busy();
		var request = new Ajax.Request(
			url,
			{ asynchronous:false }
		)
		var data = eval('('+request.transport.responseText+')');
		if (data.error){
			Kato.Error(data.error)
			throw 'invalid data'
			return
		} else {
			this.data = data
			if (!this.after_select) {
				this.initialize_validators()
				this.initialize_mutators()
				this.after_select = true
			}
		}
		Kato.Utils.idle.idle();
		if(saved_revision)this.data.head.revision = saved_revision		
	},

	delete_from:function(row_ref){
		var row = row_ref.slice(0)
		var operation = {
			row:			row,
			command:		'delete'
		}

		this.data.rows.splice(this.get_index_by_pk(row[0]),1)
		this.notify_listeners()
		this.do_operation(operation)
	},

	insert:function(row_ref,mask){
		var row = row_ref.slice(0)
		row = this.apply_mutators(row)
		if(row.length!=this.data.columns.length){
			Kato.Error('[Error] Kato.Model, wrong parameter count on insert (' + row + ')')
		}
		if (mask !== undefined){
			var new_row = row.slice(0)
			for (var v = 0; v < mask.length; v++){
				new_row[mask[v]] = row[v]
			}
		}

		var operation = {
			row:		row
		}

		for (var a in row){
			if(row[a]==='')row[a]=null
		}

		if(row[0]==null || row[0]==''){
			row[0] = this.get_new_pk()
			this.data.rows.unshift(row)
			//this.notify_listeners()
			operation.command='insert'
		} else {
			var v = this.get_index_by_pk(row[0])
			this.data.rows[v] = row
			//this.notify_listeners()
			operation.command='update'
		}
		this.do_operation(operation);

	},

	do_operation:function(operation){
		this.operations.push(operation)
		if(this.flush_mode == 'always'){
			this.flush_operations()
		}
	},

	notify_listeners:function(){
		var view = this.view
		var ping = function(listener){if(Kato.Active[listener])Kato.Active[listener].notify(view)}
		this.listeners.each(ping)
	},
	
	get_index_by_pk:function(pk){
		var cursor = 0
		var length = this.data.rows.length
		while(cursor < length){
			if(this.data.rows[cursor][0]===pk) return cursor
			cursor++
		}
		return null
	},

	get_new_pk:function(){
		var new_pk = 0;
		for (var i = 0; i < this.data.rows.length; i++) {
			var cur = parseInt(this.data.rows[i][0])
			if (cur >= new_pk) {
				new_pk = cur + 1
			}
		}
		return String(new_pk)
	},
	
	count:function(params, server_params){
		if (server_params === undefined) server_params = []
		// eksperyment...
		var server_offset = false;
		for (var v=0; v < server_params.length; v++){
			if(server_params[v]=='offset'){
				server_offset=true
				break;
			}
		}
		return this.data.rows.length + (server_offset? params.offset : 0)
		//return this.select(count_params, server_params).rows.length
	},
	
	col_name_to_index:function(col_name){
		for (var v=0; v<this.data.columns.length; v++){
			if (this.data.columns[v].column == col_name) return v;
		}
		return false
	},
	
	hash_from_row:function(row){
		var hash = {}
		for (var v=0; v < row.length; v++){
			hash[this.data.columns[v].column] = row[v]
		}
		return hash
	},

	to_index:function(val){
		return isNaN(parseInt(val)) ? this.col_name_to_index(val) : val
	}
});

Kato.Container = Class.create();
Kato.Container.prototype = Object.extend({},{
	switchboard: undefined,
	panels: undefined,
	
	initialize:function(element){
		element = $(element)
		this.element = $(element);
		this.my_initialize();
	},
	
	my_initialize:function(){
	},

	build:function(){
		this.id = this.element.id
		this.panels = [];
		this.panel_displayed = null
		var temp = this.element.childNodes;
		for (var v=0; v < temp.length; v++){
			if(!temp[v].getAttribute)continue;
			this.panels.push({
				name:		temp[v].getAttribute('kato_name')?temp[v].getAttribute('kato_name'):temp[v].id,
				element:	temp[v],
				id:			temp[v].id,
				available:	temp[v].getAttribute('kato_available')?eval(temp[v].getAttribute('kato_available')):true
			})
		}
		this.switchboard = document.createElement('div');
		this.switchboard.className='switchboard'
		this.element.insertBefore(this.switchboard,this.element.firstChild);
		if(this.panel_displayed==null){
			this.panel_displayed=0
		}
		this.show_panel(0)
	},
	panel_available:function(panel, is_available){
		for (var v=0; v < this.panels.length; v++){
			if(v==panel || this.panels[v].id == panel){
				this.panels[v].available = is_available
			}
		}
		this.render()
	},
	
	render:function(){
		var o = '';
		for (var v=0; v < this.panels.length; v++){
			if(!this.panels[v].available)continue
			var classname = ((v==this.panel_displayed) ? 'selected' : '')
			o += '<a href="#" class="' + classname + '" onclick="Kato.Active.' + this.id + '.show_panel(' + v + '); return false">'
			o += this.panels[v].name
			o += '</a> '
		}
		this.switchboard.innerHTML = o
	},
	show_panel:function(which){
		for (var v=0; v < this.panels.length ; v++){
			this.panels[v].element.style.display = 'none'
		}
		this.panels[which].element.style.display = ''
		this.panel_displayed = which
		this.render()
	}
});


Kato.View = Class.create();
Kato.View.prototype = Object.extend ({},{
	params:				undefined,
	server_params:			undefined,
	change_handlers:		undefined,
	model:				undefined,
	viewname:			undefined,
	id:				undefined,
	value:				undefined,
	text:				undefined,
	onclick_notify_children:	undefined,
	params_template:		undefined,
	changed_rows:			undefined,
	deaf:				false,
	blocks:				undefined,
	column_displayed:		undefined,
	column_displayed_index:		undefined,
	
	component_template:undefined,
	
	
	initialize:function(element){
		element = $(element)
		this.element = $(element);

		this.my_initialize()
		
		if (element.hasAttribute('kato_model')){
			this.viewname = element.getAttribute('kato_model')
		} else {
			this.viewname = 'dummy_model'
		}
		
		if (element.hasAttribute('kato_cell_binding')){
			this.cell_binding = eval('([' + element.getAttribute('kato_cell_binding') + '])')
		}
		if (element.hasAttribute('kato_params_constraint')){
			this.params_constraint = eval('({' + element.getAttribute('kato_params_constraint') + '})')
		}
		if (element.hasAttribute('kato_value')){
			this.value = element.getAttribute('kato_value')
		} else {
			this.value = ''
		}
		if (element.hasAttribute('kato_text')){	// używany przez Kato.Combo do trzymania tekstu inputa
			this.text = element.getAttribute('kato_text')
		} else {
			this.text = ''
		}
		if(this.params_template===undefined){
			this.params_template = {}
		}
		if (element.hasAttribute('kato_params_template')){
			Object.extend(this.params_template, eval('({' + element.getAttribute('kato_params_template') + '})'))
		}
		if (element.hasAttribute('kato_server_params')){
			this.server_params = eval('([' + element.getAttribute('kato_server_params') + '])')
		}
		if (element.hasAttribute('kato_params_translation_table')){
			this.params_translation_table = eval('({' + element.getAttribute('kato_params_translation_table') + '})')
		}
		if (element.hasAttribute('kato_onclick_notify_children')){
			this.onclick_notify_children = eval('([' + element.getAttribute('kato_onclick_notify_children') + '])')
		}
		this.change_handlers = Kato.Extract(element,'kato_onchange_handlers',[])
		this.onsent_handlers = Kato.Extract(element,'kato_onsent_handlers',[])
		this.afterfetch_handlers = Kato.Extract(element,'kato_afterfetch_handlers',[])
	
		this.user_initialize()		
	},
	
	user_initialize:function(){
		
	},
	
	
	destroy:function(){
		this.model.remove_listener(this)
	},
	
	move_into:function(what,before){
		if(before==null) before = $(what).firstChild
		$(what).insertBefore(this.element,before)
		this.element.style.display = ''
		this.focus()
	},
	
	hide:function(){
		this.element.display = 'none'
	},
	
	focus:function(){
		var inputs = this.element.getElementsByTagName('input');
		if(inputs.length>0){
			inputs[0].focus()
		}
	},
	
	build:function(element){
		this.params = {}
		this.model = Kato.GetModel(this.viewname)
		this.changed_rows = $H()
		this.id = this.element.id
		if(this.cell_binding && this.cell_binding[2]===undefined)this.cell_binding[2]=0
		if(!this.onclick_notify_children)this.onclick_notify_children = new Array()
		if(!this.server_params)this.server_params = new Array()
		
		this.model.add_listener(this.id)
		if(this.add_mutators) this.add_mutators()
		if(this.add_validators) this.add_validators()
		this.reset_params()
		if(this.element.hasAttribute('kato_blocks')){
			this.blocks = Kato.Extract(this.element, 'kato_blocks', {})
		}
		if(this.element.hasAttribute('kato_column_displayed')){
			this.column_displayed=this.element.getAttribute('kato_column_displayed')
		}
	},
	
	has_block:function(block_name){
		if(this.blocks==undefined) return true;
		return this.blocks[block_name] != undefined ?  this.blocks[block_name] : true;
	},
	
	constraint_form_data:function(){
		var col = []
		for (param in this.params){
			if (col = param.match(/^search_(.*)$/i)){
				this.rows[0][this.model.col_name_to_index(col[1])] = this.params[param]
			}
		}
	},
	
	send_data:function(){
		if (this.before_send_data){
			this.before_send_data()
		}

		var temporary_mode = this.model.flush_mode
		
		if (this.hide_custom_error_messages) this.hide_custom_error_messages()
		this.hide_error_messages()
		
		
		if(!this.validate_rows()){
			return false
		}

		this.model.flush_mode = 'none'
		this.deaf = true;
		this.changed_rows.each(function(row_changed){
			this.model.insert(this.rows[row_changed.key])
		}, this)
		this.model.notify_listeners();
		this.deaf = false;
		if (this.changed_rows.size()) {
			this.model.flush_operations()
		}
		this.model.flush_mode = temporary_mode
		
		this.changed_rows = $H()
		if (this.after_send_data) this.after_send_data()
		return true
	},

	validate_rows:function() {
		var validated = true
		this.changed_rows.each(function(row_changed){
			if(!this.validate_row(row_changed.key)){
				validated = false 
			}
		}, this)
		return validated
	},
	
	validate_row:function(row_index) {
		var errors = this.model.validate(this.rows[row_index])
		if (errors.length == 0) {
			return true
		}
		this.handle_errors(errors)
		return false
	},

	hide_error_messages:function(){},

	handle_errors:function(errors){
		for (var i = 0; i < errors.length; i++) {
			this.handle_error(errors[i])
		}
	},

	handle_error:function(error){
		alert(Kato.Translate(error.message))
	},
	
	col_name_to_index:function(v){
		return this.model.col_name_to_index(v)
	},

	
	set_form:function(data,rerender){
		for (key in data){
			this.set_form_cell(key,data[key],rerender)
		}
	},
	
	set_form_cell:function(col_name, col_value,rerender){
		this.set_cell(0,this.model.col_name_to_index(col_name),col_value,rerender)
	},
	
	set_cell:function(row,col,value,rerender){
		if(value===''){
			value = null;
		}
		this.rows[row][col] = value;
		this.changed_rows.set(row, true)
		if(rerender){
			this.render(undefined, false)
		}
	},

	notify:function(who){
		if(this.deaf) return
		this.render()
	},
	
	row_clicked:function(id,row_index){
		var params = {}
		params['search_'+this.columns[0].column] = String(id)
		this.onclick_notify_children.each(function(childname){
			Kato.Active[childname].change_params(params)		
		})
		for (var v=0; v<this.change_handlers.length; v++){
			this.change_handlers[v](this,id,row_index)
		}
	},
	
	translate_params:function(params){
		params_translated = {}
		for (k in params) {
			var nowe_k = k
			if (this.params_translation_table && this.params_translation_table[k]) nowe_k = this.params_translation_table[k]
			params_translated[nowe_k] = params[k]
		}
		return(params_translated)
	},

	change_params:function(params){
		this.render(this.translate_params(params))
	},
	
	reset_params:function(){
		this.params = Object.extend({},this.params_template)
	},

	reset_orderby_params: function() {
		var newparams = {};
		$H(this.params).each(function(pair) {
			if (!pair[0].match(/^orderby_/)) {
				newparams[pair[0]] = pair[1];
			}
		});
		this.params = newparams;
	},

	remove_from_params:function(params){
		if (params.constructor === String) params = [ params ];
		var rerender = false
		var newparams = {}
		$H(this.params).each(function (pair) {
			if (!params.include(pair[0])) {
				newparams[pair[0]] = pair[1]
			}
			else {
				rerender = true
			}
		})
		if (rerender) {
			this.params = newparams
			this.render()
		}
	},

	fetch_data:function(params, server_params){
		var params_constrained = Object.extend(Object.extend({},params),this.params_constraint)
		Kato.Log(params_constrained)
		var deaf_holder = this.deaf;
		this.deaf=true;
		var data = this.model.select(params, server_params)
		this.count = this.model.count(params, server_params)
		this.deaf=deaf_holder;
		//alert(this.count)
		this.rows = data.rows
		this.head = data.head
		this.columns = data.columns
		if(this.initialize_cell_renderers) this.initialize_cell_renderers()
		if(this.initialize_visible_columns) this.initialize_visible_columns()
		if(this.initialize_column_order) this.initialize_column_order()
		for(var v=0; v < this.cell_renderers.length; v++ ){
			if(this.cell_renderers[v]){
				this.first_cell_renderer_index = v
				break;
			}
		}
		if(this.column_displayed_index==undefined){
			if(this.column_displayed!=undefined){
				this.column_displayed_index = this.model.col_name_to_index(this.column_displayed)
			} else {
				this.column_displayed_index=this.first_cell_renderer_index
			}
		}
		if(this.after_fetch_data) this.after_fetch_data()
		for (var v=0; v<this.afterfetch_handlers.length; v++){
			this.afterfetch_handlers[v].bind(this)()
		}
	},
	
	initialize_cell_renderers:function(){
		this.cell_renderers = new Array();
		var user_cell_renderers = Kato.Extract(this.element, 'kato_custom_cell_renderers', {})
		
		for (var v=0; v<this.columns.length; v++){
			if(user_cell_renderers[this.columns[v].column]){
				this.cell_renderers[v] = (typeof user_cell_renderers[this.columns[v].column] == 'function' ?
							  user_cell_renderers[this.columns[v].column].bind(this) :
							  user_cell_renderers[this.columns[v].column])
			} else {
				this.cell_renderers[v] = this.build_cell.bind(this)
			}
		}
	},

	initialize_visible_columns:function(){
		var user_visible_columns = Kato.Extract(this.element, 'kato_visible_columns', [])
		if(user_visible_columns.length==0){
			for (var v=0; v<this.columns.length; v++){
				if(this.columns[v].column.match(Kato.Config.KeyColumnRegex) || this.columns[v].column == 'custom_columns'){
					this.cell_renderers[v] = null
				}
			}			
		}
		else {
			for (var v=0; v<this.columns.length; v++){
				if(!user_visible_columns.map(this.model.to_index.bind(this.model)).include(v)){
					this.cell_renderers[v] = null
				}
			}
		}
		
	},

	initialize_column_order:function(){
		var column_order = Kato.Extract(this.element, 'kato_column_order', [])
		var column_order_map = []
		if (column_order.size() == 1 && column_order[0] == 'same_as_visible_columns' && this.element.hasAttribute('kato_visible_columns')) {
			column_order = Kato.Extract(this.element, 'kato_visible_columns', [])
		}
		column_order.each(function (elem) { var t = this.model.to_index.bind(this)(elem); if (t !== false) column_order_map.push(t) }, this)
		this.columns.length.times(function (n) { if (!column_order_map.include(n)) column_order_map.push(n) })
		this.column_order_map = column_order_map
	},

	add_mutators:function(){
		var user_custom_mutators = Kato.Extract(this.element, 'kato_custom_mutators', {})
		var model = this.model
		$H(user_custom_mutators).each(function (pair) {
			$A(typeof pair.value == 'function' ? [ pair.value ] : pair.value).each(function (mutator) {
				model.add_mutator(pair.key, mutator)
			})
		})
	},

	add_validators:function(){
		var user_custom_validators = Kato.Extract(this.element, 'kato_custom_validators', {})
		var model = this.model
		var a = this.viewname
		$H(user_custom_validators).each(function (pair) {
			$A(typeof pair.value == 'function' ? [ pair.value ] : pair.value).each(function (validator) {
				model.add_validator(pair.key, validator)
			})
		})

		var user_novalidate_columns = Kato.Extract(this.element, 'kato_novalidate_columns', [])
		user_novalidate_columns.each(function(elem) { this.validators[this.to_index(elem)] = [] }, this.model)
	},

	build_cell:function(row,index){
		var o = '';
		o += '	<td>'
		o += (['', null ].include(row[index])) ? '&nbsp;' : row[index]
		o += "</td>\n"
		return o
	},
	
	push_empty_row:function(){
		var empty_row = new Array();
		for (var v=0; v < this.columns.length; v++){
			empty_row.push('')
		}
		this.rows.unshift(empty_row)
		this.rows[0][0] = null
	},
	
	render:function(params, fetch_data){
		if(fetch_data==undefined) fetch_data = true
		if (params===undefined) var params = {}
		this.params = Object.extend(this.params,params)
		if(fetch_data){
			this.fetch_data(this.params, this.server_params)
		}
		if(this.rows.length==0){
			this.push_empty_row()
			if(this.constraint_form_data) this.constraint_form_data()
		}
		this.build_html();
		this.element.innerHTML = this.built_html
		if(this.after_render)this.after_render()
		if(this.custom_after_render) this.custom_after_render.bind(this)()
	}
});

Kato.Dropdown = Class.create();
Kato.Dropdown.prototype = Object.extend({},Kato.View.prototype)

Object.extend(Kato.Dropdown.prototype, {	
	value:null,
	null_option:undefined,

	my_initialize:function(){
		this.null_option = Kato.Extract(this.element, 'kato_null_option', true, true)
	},
	
	build_html:function(){
		this.built_html = '';
		this.built_html += this.build_header()
		this.built_html += this.build_rows()
		this.built_html += this.build_footer()
	},
	
	build_header:function(){
		var o = ''
		o += '<select onclick="return false" onchange="Kato.Active.' + this.id + '.row_clicked_dropdown(this.options[this.selectedIndex].value)" id="' + this.id + 'Kselect">'
		return o
	},
	
	row_clicked_dropdown:function(id){
		this.value = id
		if(this.cell_binding){
			Kato.Active[this.cell_binding[0]].set_cell(this.cell_binding[2],this.cell_binding[1],id)
		}
		this.row_clicked(id)
	},
	
	build_footer:function(){
		var o = '';
		o += '</select>'
		return o
	},
	
	build_rows:function(){
		var o = ''
		if (this.null_option) {
			o += '<option value="">---</option>'
		}
		else {
			if (this.cell_binding && this.rows[0] && this.rows[0][0]) {
				Kato.Active[this.cell_binding[0]].set_cell(this.cell_binding[2],this.cell_binding[1],this.rows[0][0])
			}
		}
		for(var v=0; v< this.rows.length; v++){
			o += this.build_row(this.rows[v],v)
		}
		return o;
	},
	
	build_row:function(row){
		if (!row[0]) return
		var o = ''
		o += '<option value="' + row[0] + '"'
		if (this.value == row[0]) o += " selected='selected' "
		o += '>'
		o += this.cell_renderers[this.column_displayed_index](row,this.column_displayed_index)
		o += "</option>\n"
		return o;
	},
	
	build_cell:function(row,index){
		return row[index]
	}
})


Kato.Detail = Class.create();
Kato.Detail.prototype = Object.extend({},{
	
	my_initialize:function(){
		this.params_template = {offset: 0, limit:1}		
	},
	
	build_html:function(){
		this.built_html = '';
		this.built_html += this.build_controls()
		this.built_html += this.build_header()
		this.built_html += this.build_rows()
		this.built_html += this.build_footer()
	},
	
	build_header:function(){
		var o = '';
		o += '<table class="kato_detail kato">'
		return o
	},
	
	build_rows:function(){
		var o = '';
		for(var v=0; v< this.columns.length; v++){
			var vm = this.column_order_map[v]
			if(this.cell_renderers[vm]){
				o += this.build_row(this.rows[0],vm)
			}
		}
		return o
	},
	
	build_footer:function(){
		var o = '';
		o += '</table>'
		return o
	},
	
	build_controls:function(){
		var o = '';
		return o
	},

	build_row:function(row,index){
		var o = '';
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += this.cell_renderers[index](row,index,this.id,0)
		return o
	}
});

Object.extend(Kato.Detail.prototype,Kato.View.prototype)

Kato.Form = Class.create()
Kato.Form.prototype = Object.extend({},Kato.Detail.prototype)
Kato.Form.prototype = Object.extend(Kato.Form.prototype,{
	my_initialize:function(){
		this.params_template = {offset: 0, limit:1}
	},

	send_form:function(){
		this.deaf = true;
		try {
			var is_insert = this.rows[0][0] === null
			if(this.send_data()){
				if (is_insert) {
					this.clear_form()
				}
				if (this.after_operation) this.after_operation('send')
			}
			for (var v=0; v<this.onsent_handlers.length; v++){
				this.onsent_handlers[v].bind(this)()
			}
		} catch (e) {
			this.deaf = false;
		}
	},

	send_form_as_new:function(){
		var oldid = this.rows[0][0]
		this.rows[0][0] = null
		if(this.send_data()){
			this.clear_form()
			if (this.after_operation) this.after_operation('send')
		}
		else {
			this.rows[0][0] = oldid
		}
		for (var v=0; v<this.onsent_handlers.length; v++){
			this.onsent_handlers[v].bind(this)()
		}
	},

	handle_error:function(errorspec) {
		var done = false
		if (this.custom_handle_error) {
			done = this.custom_handle_error(errorspec)
		}
		if (done) return

		/* col is the index in the this.columns array pointing to the column in which the error occurred */
		var col = this.column_order_map.indexOf(errorspec.column)
		/* field is the number of non-null cell renderers that precede the renderer of that column */
		var field = $R(0, col, true).inject(0, function(acc, val) {
			/* this is straightforward: we collect the number of non-null cell renderers
			 * by looking at the this.cell_renderers array under indices from 0 to col inclusive
			 * after translating them with this.column_order_map
			 */
			if (this.cell_renderers[this.column_order_map[val]]) { return acc + 1 }
			else { return acc }
		}, this)
		if (this.cell_renderers[field]) {
			var input = this.element.descendants().select(function (elem) {
				return ['input', 'select', 'textarea'].include(elem.tagName.toLowerCase())
			})[field]
			input.style.backgroundColor = '#FF9595'
		}
		var li = $(this.errors_list).descendants()[col]
		li.innerHTML = ''
		li.innerHTML += Kato.Translate(this.columns[errorspec.column].column)
		li.innerHTML += ': '
		li.innerHTML += errorspec.message
		li.show()
		return true
	},

	hide_error_messages:function() {
		$(this.errors_list).descendants().each(Element.hide)
		$A(this.element.getElementsByTagName('input')).each(function (elem) {elem.style.backgroundColor = ''})
	},

	hide_custom_error_messages:function() {
	},

	delete_form:function(){
		if (this.rows[0][0]){
			this.model.delete_from(this.rows[0])
		}
		this.clear_form()
		if (this.after_operation) this.after_operation('delete')
		for (var v=0; v < this.onsent_handlers.length; v++){
			this.onsent_handlers[v].bind(this)()
		}
	},
	
	clear_form:function(rerender){
		if(rerender==undefined) rerender = true
		
		var old_row = this.rows[0]
		this.rows[0] = new Array()

		for (var v = 0; v < this.columns.length; v++){
			this.rows[0].push(null)
		}

		this.constraint_form_data()
		this.rows[0][0] = null
		if(rerender){
			this.build_html();
			this.element.innerHTML = this.built_html
			if(this.after_render)this.after_render()
			if(this.custom_after_render) this.custom_after_render.bind(this)()
		}
	},
	
	build_row:function(row,index){
		var value = row[index]===null?'':row[index]
		if (typeof(this.cell_renderers[index]) == 'function') {
			return this.cell_renderers[index].bind(this)(row, index, this.id, 0, value)
		}
		/* renderer jest stringiem, czyli np. dropdownem. Trzeba stworzyć dla niego puste td */
		return this.build_cell(row, index)
	},

	build_cell:function(row, index, id, row_index, value) {
		var o = ''
		o += '<tr><th style="width: 30%">' + Kato.Translate(this.columns[index].column) + '</th>'
		o += '<td id="' +this.id + 'Krow_' + this.columns[index].column + '"><input class="text" onkeyup="Kato.Active.' + this.id + '.set_cell(0,' + index + ',this.value)" value="' + (value ? Kato.Utils.escape_quotes(value) : '') + '"/>'
		o += '</td></tr>'
		if (value!==undefined) {
			this.set_cell(0, index, value)
		}
		return o
	},

	build_footer:function(){
		var o = '';
		o += '<tr><td colspan="2">'		
		
		if(this.has_block('form_edit'))
			o += '<input type="button" class="kato_button" value="' + Kato.Translate('edit') + '" onclick="Kato.Active.' + this.id + '.send_form()" />'		
		if(this.has_block('form_save_new'))
			o += '<input type="button" class="kato_button" value="' + Kato.Translate('save_new') + '" onclick="Kato.Active.' + this.id + '.send_form_as_new()" />'
		if(this.has_block('form_new_item'))
			o += '<input type="button" class="kato_button" value="' + Kato.Translate('new_item') + '" onclick="Kato.Active.' + this.id + '.clear_form()" />'
		if(!this.has_block('no_form_careful_new_item'))
			o += '<input type="button" class="kato_button" value="' + Kato.Translate('new_item') + '" onclick="if(confirm(Kato.Translate(\'confirm_new_item\')))Kato.Active.' + this.id + '.clear_form()" />'
		if(this.has_block('form_remove'))
			o += ' | <input type="button" class="kato_button" value="' + Kato.Translate('remove') + '" onclick="Kato.Active.' + this.id + '.delete_form()" />'
		if(!this.has_block('no_form_careful_remove'))
			o += ' | <input type="button" class="kato_button" value="' + Kato.Translate('remove') + '" onclick="if(confirm(Kato.Translate(\'confirm_remove\')))Kato.Active.' + this.id + '.delete_form()" />'
		if(typeof(this.has_block('form_custom_buttons'))=='function'){
			o += this.has_block('form_custom_buttons').bind(this)()
		}
		o += '</td></tr>'
		o += '<tr><td colspan="2"><ul>'
		o += '<li class="error_message" style="display: none;"></li>'.times(this.columns.length)
		o += '</ul></td></tr>'
		o += '</table>'
		return o
	},

	after_render:function(){
		for(var v = 1; v < this.columns.length; v++){
			if(this.cell_renderers[v]=='dropdown' || this.cell_renderers[v]=='combo'){
				var params = {}
				params.id = this.id + 'Kdropdown_' + this.columns[v].column
				params.model = Kato.PrimaryKeys[this.columns[v].column]
				if(!Kato.PrimaryKeys[this.columns[v].column]){
					Kato.Error('wrong model for key ' + this.columns[v].column)
				}
				params.type = this.cell_renderers[v]=='dropdown'?'Kato.Dropdown':'Kato.Combo'
				params.cell_binding= "'" + this.id + "'," + v
				Kato.BuildFromScratch(params,$(this.id + 'Krow_' + this.columns[v].column))
			}
		}
		if(this.custom_after_render) this.custom_after_render.bind(this)()
		for(var v = 1; v < this.columns.length; v++){
			if(this.cell_renderers[v]=='dropdown' || this.cell_renderers[v]=='combo'){
				var id = this.id + 'Kdropdown_' + this.columns[v].column
				Kato.Active[id].value = this.rows[0][v]
				Kato.Active[id].render()
			}
		}
		this.errors_list = this.element.getElementsByTagName('ul')[0]
	},
	custom_after_render:function(){
	}
})

Kato.Grid = Class.create();
Kato.Grid.prototype = Object.extend ({},{
	page:				0,
	
	my_initialize:function(){
		if(!this.params_template){
			this.params_template = {offset: 0, limit:50}
		}
		this.column_widths = Kato.Extract(this.element,'kato_column_widths',{})
		this.clickable_column_headers = Kato.Extract(this.element, 'kato_clickable_column_headers', true, true);
	},
	
	page_flip_next:function(){
		if(!this.rows.length) return;
		this.send_data()
		this.page++
		this.render()
	},
	
	page_flip_previous:function(){
		if(this.page<=0)return;
		this.send_data()
		this.page--
		this.render()
	},

	
	before_render:function(){
		this.save_th_widths()
	},
	
	after_render:function(){
		this.set_th_widths()

		var view = this;
		var change_sorting = function() {
			col = parseInt(this.getAttribute('column_index'));
			var params = {};
			if (view.params['orderby_'+view.columns[col].column] === undefined ||
				view.params['orderby_'+view.columns[col].column] == 'desc') {

				params['orderby_'+view.columns[col].column] = 'asc';
			} else {
				params['orderby_'+view.columns[col].column] = 'desc';
			}
			view.reset_orderby_params();
			view.change_params(params);
		}

		if (this.clickable_column_headers) {
			ths = this.element.getElementsByTagName('th');
			for (var i=0; i < ths.length; i++) {
				$(ths[i]).observe('click', change_sorting);
			}
		}
	},
	
	save_th_widths:function(){
		this.saved_th_widths = []
		var ths = this.element.getElementsByTagName('th')
		for (var v=0; v < ths.length; v++){
			this.saved_th_widths[v] = Element.getWidth(ths[v])-8
			this.saved_th_widths[v] = (v % 2 == 0) ? 20 : 40
		}
	},
	
	set_th_widths:function(){
		var ths = this.element.getElementsByTagName('th')
		if (!this.saved_th_widths){
			for (var v=0; v < ths.length; v++){
				//ths[v].style.width = Element.getWidth(ths[v])-4
			}
		} else {
			for (var v=0; v < ths.length; v++){
				if(this.saved_th_widths[v]) {
//					alert(this.id + " : " + this.saved_th_widths[v])
					//ths[v].style.width = this.saved_th_widths[v]
				}
			}
			
		}
	},
	
	insert_new_row:function(){
		if(this.rows[0][0]==null) return
		this.push_empty_row()
		this.render({},false)
	},
	
	build_html:function(){
		this.built_html = '';
		if(this.has_block('grid_controls')==true){
			this.built_html += this.build_controls()
		}
		this.built_html += this.build_header()
		this.built_html += this.build_rows()
		this.built_html += this.build_footer()
		if(this.has_block('grid_controls')=='bottom'){
			this.built_html += this.build_controls()
		}
	},	

	build_header:function(){
		var o = '<div style="overflow: auto"><table class="kato kato_table">\n';
		if(this.has_block('grid_header')){
			var ths = '<tr>'
			this.number_of_columns = 0;
			for (var v = 0; v < this.columns.length; v++) {
				var vm = this.column_order_map[v]
				if (this.cell_renderers[vm]) {
					ths += '<th';
					if ((this.column_widths) && (this.column_widths[this.columns[vm].column])) {
	//				alert(this.column_widths[this.columns[v].column]);
						ths += ' style="width: ' + this.column_widths[this.columns[vm].column]
						if (this.column_widths[this.columns[vm].column].match(/\d$/)) {
							 ths += 'px"';
						}
						else {
							ths += '"';
						}
					} 
					ths += ' column_index="' + vm + '"';
					ths += '>';
	//				if (v!=this.columns.length-1) ths += '<span onmousedown="Kato.Resize(this.parentNode,Event.pointerX(event))" style="position: relative; float: right; height: 100%; width: 10px; cursor:e-resize"></span>'
					ths += Kato.Translate(this.columns[vm].column);
					if (this.params['orderby_'+this.columns[vm].column] !== undefined) {
						if (this.params['orderby_'+this.columns[vm].column] == 'desc') {
							ths += '&nbsp;&#9650;';
						} else {
							ths += '&nbsp;&#9660;';
						}
					}
					ths += '</th>';
					this.number_of_columns++
				}
			}
			o += ths
			o += '</tr>'
		}
		return o;
	},
	
	build_rows:function(){
		var o = ''
		for(var v=0; v< this.rows.length; v++){
			o += this.build_row(this.rows[v],v)
		}
		return o;
	},

	build_row:function(row,row_number){
		var o = ''
		if(this.has_block('grid_highlight_rows')){
			o += "<tr style='cursor:pointer' onclick='Kato.Active." + this.id + ".row_clicked(\""+row[0]+"\", " + row_number + ")' onmouseout='this.style.backgroundColor=\"\"' onmouseover='this.style.backgroundColor=\"#EEE\"'>\n"
		} else {
			o += "<tr>\n"
		}
		for(var v=0; v<row.length; v++){
			var vm = this.column_order_map[v]
			if(this.cell_renderers[vm]){
				o += this.cell_renderers[vm](row,vm,this.id, row_number);
			}
		}
		o += "</tr>\n"
		return o
	},	
	
	build_footer:function(){
		var o = ''
		o += '</table></div>'
		
		return o;
	},
		
	build_controls:function(){
		var o = '';
		if (this.page > 0){
			o += "<a href='' onclick='Kato.Active."+this.id+".page_flip_previous(); return false'>" + Kato.Translate('prev_page')+ "</a>"
		} else {
			o += Kato.Translate('prev_page')
		}
		o += ' | '
		if (this.page < this.last_page){
			o += "<a href='' onclick='Kato.Active."+this.id+".page_flip_next(); return false'>" + Kato.Translate('next_page')+ "</a>"
		} else {
			o += Kato.Translate('next_page')
		}
		return o
	}
});

Object.extend(Kato.Grid.prototype,Kato.View.prototype)

Kato.Grid.prototype.change_params=function(params){
		this.page = 0
		this.render(this.translate_params(params))
}

Kato.Grid.prototype.render=function(params, fetch_data){
		this.before_render()
		if(fetch_data==undefined) fetch_data = true
		if (params===undefined) var params = {}
		this.params = Object.extend(this.params,params)
		this.params.offset = this.page * this.params.limit
		if(fetch_data) this.fetch_data(this.params, this.server_params)
		this.last_page = Math.floor(this.count / this.params.limit)
		this.build_html();
		this.element.innerHTML = this.built_html
		this.after_render()
	if(this.custom_after_render) this.custom_after_render.bind(this)()
}

Kato.Combo = Class.create()
Kato.Combo.prototype = Object.extend({},{

	value:null,
	min_search_chars:undefined,
	selected_option:undefined,
	key_handled:false,
	
	my_initialize:function(){
		this.min_search_chars = Kato.Extract(this.element, 'kato_min_search_chars', '')
	},	

	build_html:function(){
		if(this.value === null)this.text = ''
		this.built_html = '';
		this.built_html += this.build_header()
		this.built_html += this.build_footer()
	},
	
	build_header:function(){
		var o = ''
		o += '<input onfocus="this.select()" onkeydown="Kato.Active.' + this.id + '.key_down()" onkeypress="Kato.Active.' + this.id + '.key_pressed(event)" onkeyup="Kato.Active.' + this.id + '.combo_text_changed(this,event)" value="' + this.text + '" type="text" /><br />'
		//o += '<input onfocus="this.select()" onkeyup="Kato.Active.' + this.id + '.combo_text_changed(this,event)" value="' + this.text + '" type="text" /><br />'
		o += '<select size="4" style="z-index: 10; position: absolute; display: none" onchange="Kato.Active.' + this.id + '.row_clicked_combo(this.options[this.selectedIndex].value,this.options[this.selectedIndex].text)" id="' + this.id + '_select">'

		return o
	},

	handle_special_keys:function(code){

		switch (code) {

		case Event.KEY_DOWN:
			if ($(this.id + '_select').style.display == 'none') return
			if (!this.selected_option && $(this.id + '_select').firstDescendant()) {
				this.selected_option = $(this.id + '_select').firstDescendant()
			}
			else if (this.selected_option.nextSibling) {
				this.selected_option = $(this.selected_option.nextSibling)
			}
			if (this.selected_option) {
				this.selected_option.selected = true
			}
			this.key_handled = true
			break

		case Event.KEY_UP:
			if (this.selected_option && this.selected_option.previousSibling) {
				this.selected_option = $(this.selected_option.previousSibling)
				this.selected_option.selected = true
			}
			this.key_handled = true
			break

		case Event.KEY_RETURN:
			if (this.selected_option) {
				this.row_clicked_combo($(this.id + '_select').options[$(this.id + '_select').selectedIndex].value,$(this.id + '_select').options[$(this.id + '_select').selectedIndex].text)
			}
			this.selected_option = undefined
			this.key_handled = true
			break

		case Event.KEY_RIGHT:
		case Event.KEY_LEFT:
			this.key_handled = true
			break

		case Event.KEY_ESC:
			$(this.id + '_select').style.display='none'
			this.selected_option = undefined
			this.key_handled = true
			break
		}
	},

	key_down:function(){
		this.key_handled = false
	},

	key_pressed:function(event){
		if ($A([Event.KEY_DOWN, Event.KEY_UP]).include(event.keyCode)) {
			this.handle_special_keys(event.keyCode)
			this.key_handled = true
		}
	},

	combo_text_changed:function(input,event){
		if (this.key_handled) return
		this.handle_special_keys(event.keyCode)
		if (this.key_handled) return

		this.value = null
		this.selected_option = undefined

		this.params['regexp_' + this.columns[this.column_displayed_index].column] = input.value
		if(input.value != '' && (!this.min_search_chars || (input.value.length > this.min_search_chars))){
			$(this.id + '_select').style.display=''
			this.fetch_data(this.params)
		} else {
			$(this.id + '_select').style.display='none'
		}
		if (this.rows.length){
			this.redraw_options()
		} else {
			$(this.id + '_select').style.display='none'
		}
	},

	redraw_options:function(){
		var select = $(this.id + '_select')
		while(select.options.length>0){
			select.options[0]=null;
		}
		
		var id_on_list = false;
		
		for (var v=0; v < this.rows.length; v++){
			select.options[select.options.length] = new Option (this.rows[v][this.column_displayed_index],this.rows[v][0])
			if (this.rows[v][0] == this.value && this.value !== null){
				this.text = this.rows[v][this.column_displayed_index]
				this.text = (this.rows[v][this.column_displayed_index])
				select.previousSibling.previousSibling.value = this.text
				id_on_list = true;
			}
		}
		if (!id_on_list){
			this.text = '';
			if(this.cell_binding){
				Kato.Active[this.cell_binding[0]].set_cell(this.cell_binding[2],this.cell_binding[1],null)
			}
		}
	},

	after_render:function(){
		this.redraw_options();
	},

	row_clicked_combo:function(id,text){
		this.value = id
		$(this.id + '_select').previousSibling.previousSibling.value = text
		if(this.cell_binding){
			Kato.Active[this.cell_binding[0]].set_cell(this.cell_binding[2],this.cell_binding[1],id)
		}
		this.row_clicked(id)
		/* if the combo is being injected the element containing the options has been already destroyed, do not try to hide it */
		if ($(this.id + '_select')) {
			$(this.id + '_select').style.display = 'none'
		}
	},
	
	build_footer:function(){
		var o = '';
		o += '</select>'
		return o
	}
})
Kato.Combo.prototype = Object.extend(Kato.Combo.prototype,Kato.View.prototype)

Kato.Pivot = new Class.create()
Kato.Pivot.prototype = Object.extend({}, Kato.Grid.prototype)
Object.extend(Kato.Pivot.prototype, {
	my_initialize:function(){
		this.hierarchy = Kato.Extract(this.element,'kato_pivot_hierarchy',[])
		var renderers = Kato.Extract(this.element,'kato_pivot_renderers',{})
		this.column_widths = Kato.Extract(this.element,'kato_column_widths',{})
		this.pivot_renderers = {}
		for (var v=0; v < this.hierarchy.length; v++){
			if(renderers[this.hierarchy[v]]){
				this.pivot_renderers[this.hierarchy[v]] = renderers[this.hierarchy[v]].bind(this)
			} else {
				this.pivot_renderers[this.hierarchy[v]] = this.build_pivot.bind(this)		
			}
		}
		
	},
	
	build_html:function(){
		this.built_html = '';
		this.built_html += this.build_rows()
		this.built_html += this.build_footer()
	},	

	
	build_pivot:function(row,row_index,column){
		var napis = row[column] ? row[column] : '...'
		return '<tr class="kato_pivot_row kato_pivot_' +this.id + '_' + column + '"><td class="kato_pivot_row_td" colspan="' + this.number_of_columns + '">' + napis  + '</td></tr>'
	},
	
	row_still_mine:function(first_row, row, hierarchy_index){
		if(hierarchy_index==-1){
			return true;
		}
		var v=0
		while(v <= hierarchy_index){
			var col = this.model.to_index(this.hierarchy[v]);
			if(row[col]!=first_row[col]){

				return false
			}
			v++;
		}
		return true;
	},
	
	
	build_rows:function(hierarchy_index, row_index){
		//alert(this.build_current_hierarchy(this.rows[0],1))
		// inicjalizacja
		
		var o = ''
		
		// pierwsze wywołanie:
		
		if(hierarchy_index==undefined) hierarchy_index = -1
		if(row_index==undefined) row_index = 0

		//alert([hierarchy_index, row_index])
		
		// wyszło poza hierarchię, czyli koniec rekursji: rysuje pojedynczy wiersz

		//alert('ewaluuje: ' + [hierarchy_index,this.hierarchy.length])
		if(hierarchy_index>=this.hierarchy.length){
			if (this.rows[row_index])
			o += this.build_row(this.rows[row_index],row_index)
			return ([o, row_index+1])
		}

		// jesteśmy w obrębie hierarchii
		
		var hierarchy_column = this.model.to_index(this.hierarchy[hierarchy_index])
		var first_row = $A(this.rows[row_index]).slice()

		// pętla
		if(this.pivot_renderers[this.hierarchy[hierarchy_index]])o += this.pivot_renderers[this.hierarchy[hierarchy_index]](this.rows[row_index],row_index,hierarchy_column)
		
		//o += '<tr><td colspan="11">' + this.hierarchy[hierarchy_index] + '=' + this.rows[row_index][hierarchy_column] + '</td></tr>'
		
		while(
			(this.rows[row_index])
			&& this.row_still_mine(first_row,this.rows[row_index],hierarchy_index)
		) {
			
			var result = this.build_rows(hierarchy_index+1, row_index)
			row_index = result[1]
			o += result[0]
		}
		if(hierarchy_index==-1) return o
		return [o, row_index];
	}

/*
	build_cell:function(row,index){
		var o = ''
		o += '<td>' + row[index] + '</td>'
		return o
	}
*/
})


Kato.Stub = new Class.create()
Kato.Stub.prototype = Object.extend(Kato.Stub.prototype,{
	my_initialize:function(){
	},
	
	build_html:function(){
		this.built_html = '<h3>To jest pieniek</h3>'
	}
})
Kato.Stub.prototype = Object.extend(Kato.Stub.prototype,Kato.View.prototype)

Kato.SpeedForm = new Class.create()
Kato.SpeedForm.prototype = Object.extend(Kato.SpeedForm.prototype, Kato.View.prototype)
Kato.SpeedForm.prototype = Object.extend(Kato.SpeedForm.prototype,{
	
	my_initialize:function(){
		this.string_from_row=Kato.Extract(this.element,'kato_string_from_row',[])
		this.row_from_string=Kato.Extract(this.element,'kato_row_from_string',[])
		this.afterfetch_handlers.push(this.update_text)
	},

	update_text:function(){
		if(this.rows.length>0){
			this.text = this.string_from_row[0].bind(this)(this.rows[0])
		} else {
			this.text = ''
		}
	},

	keyup:function(text){
		this.text = text;
		this.row_from_string[0].bind(this)(text)
	},
	
	build_html:function(){
		var o = '<div class="kato_speedform">'
		o += '<input style="width: 100%" onkeyup="var i=Kato.Active.' + this.id + '; i.keyup(this.value); if(event.keyCode==Event.KEY_RETURN){i.send_data()}" value="' + this.text + '" />'
		o += '<br /><input type="button" onclick="Kato.Active.' + this.id + '.send_data()" value="' + ((this.rows[0][0]!=null) ? Kato.Translate('edit') : Kato.Translate('add')) + '"></div>'
		this.built_html = o
	}
})



Kato.Template = new Class.create()
Kato.Template.prototype = Object.extend(Kato.Template.prototype,{
	template_content: undefined,
	model_name: undefined,
	
	my_initialize:function(){
		this.template_content = this.element.innerHTML;
		if(this.element.hasAttribute('kato_model_alias')){
			this.model_name = this.element.getAttribute('kato_model_alias')
		}
	},
	
	build_html:function(){
		var o = ''
		var meta = {}
		meta.count = this.rows.length
		meta.first = true
		for (var v = 0; v < this.rows.length; v++){
			if(v==this.rows.length-1) meta.last = true
			meta.number = v + 1
			meta.row_number = v
			o += this.substitute(this.template_content, this.rows[v],meta)
			meta.first = false
		}
		this.built_html = o
	},
	
	substitute:function(content, row, meta){
		nice_row = this.model.hash_from_row(row)
		eval('var ' + (this.model_name ? this.model_name : this.model.view) + '= nice_row' )
		var substitute
		while((substitute = content.match(/\${([^}]*)}/)))
		{			
			content = content.replace(/\${([^}]*)}/,eval(substitute[1]))
		}
		while((substitute = content.match(/\$%7b(.+?)%7d/i)))
		{			
			content = content.replace(/\$%7b(.+?)%7d/i,eval(substitute[1]))
		}
		return content
	}
	
})
Kato.Template.prototype = Object.extend(Kato.Template.prototype,Kato.View.prototype)

Kato.SortableTemplate = Class.create(Kato.Template, {
	custom_after_render: function() {
		var update_function = function () {
			var col_to_update = this.model.to_index(this.ordering_column);
			if (!col_to_update)
				return;
			var children = $(this.element.id + 'Ksortable').childElements();
			for (var i=0; i < children.length; i++) {
				var e = children[i];
				var row_number = parseInt(e.getAttribute('kato_row_number'));
				this.set_cell(row_number, col_to_update, String(i)); // FIXME: i + offset?
			}
			this.send_data();
		}
		Sortable.create(this.element.id+'Ksortable', {
			onUpdate: update_function.bind(this),
			tag: this.tag
		});
	},

	my_initialize: function($super) {
		$super();
		this.ordering_column = Kato.Extract(this.element, 'kato_ordering_column', '');
		if (this.ordering_column == '') {
			this.ordering_column = 'ordering';
		}
		this.tag = Kato.Extract(this.element, 'kato_sortable_tag', '');
		if (this.tag == '') {
			this.tag = 'div';
		}
		this.params_template = {};
		this.params_template['orderby_'+this.ordering_column] = 'asc';
	},

	build_html: function($super) {
		$super();
		this.built_html = '<div id="' + this.element.id + 'Ksortable" class="sortable_template">' + this.built_html + '</div>';
	},

	substitute: function($super, content, row, meta) {
		return '<' + this.tag + ' id="' + this.element.id + 'Ksortable_' + meta.row_number + '" kato_row_number="' + meta.row_number + '">' + $super(content, row, meta) + '</' + this.tag + '>';
	}
});

Kato.Tree = new Class.create()
Kato.Tree.prototype = Object.extend({}, Kato.View.prototype)
Object.extend(Kato.Tree.prototype,{
	
	toggled_rows:	undefined,
	parent:			undefined,
	collapsed_rows:	undefined,
	default_collapsed:	true,	
	
	my_initialize:function(){
		this.parent = this.element.hasAttribute('kato_tree_parent') ? this.element.getAttribute('kato_tree_parent') : 'null'
		this.parent_column = (this.element.getAttribute('kato_tree_parent_column'))
		this.collapsed_rows = {}
	},

	build_html:function(){
		this.make_tree()
		var o = ''
		o += this.build_header();
		o += this.build_rows();
		o += this.build_footer();
		this.built_html = o
	},

	tree_toggle:function(pk){
		this.collapsed_rows[pk] = !this.collapsed_rows[pk]
		this.render()
	},

	is_collapsed:function(pk){
		if(this.collapsed_rows[pk]==undefined){
			this.collapsed_rows[pk] = this.default_collapsed
		}
		return this.collapsed_rows[pk]
	},
	
	
	build_cell:function(row,index){
		return row[index]
	},
	
	
	build_node:function(row){
		var o = ''
		var children = []
		var span_class = ''
		if(row != this.parent){
			children = this.tree[(row[0])]
			o += '<li>'
			if(children){
				if(this.is_collapsed(row[0]) && row != this.parent){
					span_class = 'kato_tree_collapsed'
				} else {
					span_class = 'kato_tree_not_collapsed'
				}
			}
			if(children){
				o += '<span class="' + span_class + '" style="cursor: pointer" onclick="Kato.Active.' + this.id + '.tree_toggle(\'' + row[0] + '\');">&nbsp;&nbsp;&nbsp;&nbsp;</span> '
			} else {
				o += '<span class="' + span_class + '"></span> '				
			}
			o += '<a href="#stub" onclick="Kato.Active.' + this.id + '.row_clicked(\'' + row[0] + '\'); return false">'
			o += this.cell_renderers[this.column_displayed_index](row,this.column_displayed_index)  + '</a></li>'
		} else {
			children = this.tree[this.parent]
		}
		if(children){
			if(this.is_collapsed(row[0]) && row != this.parent){
				o += '<li style="display: none">'
			} else {
				o += '<li>'
			}
			o += '<ul>'
			for (var v=0; v < children.length; v++){
				o += this.build_node(children[v])
			}
			o += '</ul></li>'
		}
		return o
	},
	
	build_rows:function() {
		var o = ''
		o += '<ul class="kato_tree">'
		o += this.build_node(this.parent)
		o += '</ul>'
		return o
	},
	
	make_tree:function() {
		var parent_column_index = this.model.to_index(this.parent_column)
		this.parent_column_index = parent_column_index
		this.tree = {}
		for (var v = 0; v < this.rows.length; v++){
			
//			if(this.rows[v][parent_column_index]){
				if(this.tree[this.rows[v][parent_column_index]]){
					this.tree[this.rows[v][parent_column_index]].push(this.rows[v])
				} else {
					this.tree[this.rows[v][parent_column_index]] = [this.rows[v]]
				}
//			} else {
//				this.tree.root.push(this.rows[v])
//			}
			
		}
	},
	
	build_footer:function() { return '' },
	build_controls:function() { return '' },
	build_header:function() { return '' }


})




Kato.Label = Class.create()
Kato.Label.prototype = Object.extend({}, Kato.Detail.prototype)
Object.extend(Kato.Label.prototype, {

	build_rows:function() {
		var o = ''
			for (var v = 0; v < this.columns.length; v++) {
				var vm = this.column_order_map[v]
				if (this.cell_renderers[vm]) {
					o += this.build_row(this.rows[0], vm)
					break
				}
			}
		return o
	},

	build_row:function(row, index) {
		return this.cell_renderers[index](row, index, this.id, 0)
	},

	build_footer:function() { return '' },
	build_controls:function() { return '' },
	build_header:function() { return '' }
})

Kato.Calendar = new Class.create()
Kato.Calendar.prototype = Object.extend({},Kato.View.prototype)
Object.extend(Kato.Calendar.prototype,{
	calendar_from: undefined,
	calendar_to: undefined,

	my_initialize:function(){
		if(this.element.hasAttribute('kato_timestamp_column') && !this.timestamp_column){
			this.timestamp_column = this.element.getAttribute('kato_timestamp_column')
		}
		this.onchange_date_handlers = Kato.Extract(this.element,'kato_onchange_date_handlers',[])
	},
	
	build_html:function(){
		if(this.params['ge_' + this.timestamp_column]){
			this.calendar_from = Kato.Utils.date_from_string(this.params['ge_' + this.timestamp_column])
		}
		if(this.params['lt_' + this.timestamp_column]){
			this.calendar_to = Kato.Utils.date_from_string(this.params['lt_' + this.timestamp_column])
		}
		this.built_html = ''
		this.built_html += this.render_header()
		this.built_html += this.render_days()
		this.built_html += this.render_footer()
	},
	
	render_header:function(){
		var o=''
		o += '<table class="kato kato_calendar">'
		return o
	},
	
	render_footer:function(date){
		var o = ''
		o += '</table>'
		return o
	},
	
	render_days:function(){
		var month = -1
		var o = ''
		var date = this.calendar_from ? new Date(this.calendar_from) : new Date()
		date.setDate(1)
		var max_date =  this.calendar_to ? new Date(this.calendar_to) : this.max_date
		while(date < max_date){
			if(date.getMonth()!=month){
				if(month!=-1) o += this.pad_to_end(date)
				month = date.getMonth()
				o += this.render_month_start(date)
			}
			o += this.render_day(date)
			date.setDate(date.getDate() + 1)
			if(date.getDay()==1) o += this.render_week_end(date)
		}
		o += this.pad_to_end(date)
		return o
	},

	month_forward:function(){
		this.calendar_from.setMonth(this.calendar_from.getMonth()+1)
		this.calendar_to.setMonth(this.calendar_to.getMonth()+1)
		this.rerender_with_changed_range()
	},
	
	month_back:function(){
		this.calendar_from.setMonth(this.calendar_from.getMonth()-1)
		this.calendar_to.setMonth(this.calendar_to.getMonth()-1)
		this.rerender_with_changed_range()
	},

	year_forward:function() {
		this.calendar_from.setFullYear(this.calendar_from.getFullYear()+1)
		this.calendar_to.setFullYear(this.calendar_to.getFullYear()+1)
		this.rerender_with_changed_range()
	},
	
	year_back:function() {
		this.calendar_from.setFullYear(this.calendar_from.getFullYear()-1)
		this.calendar_to.setFullYear(this.calendar_to.getFullYear()-1)
		this.rerender_with_changed_range()
	},

	rerender_with_changed_range:function() {
		this.params['ge_' + this.timestamp_column]=Kato.Utils.string_from_date(this.calendar_from)
		this.params['lt_' + this.timestamp_column]=Kato.Utils.string_from_date(this.calendar_to)
		this.render()
	},	
	
	render_month_start:function(date){
		var o = ''
		o += '<tr><th colspan="7" class="month_name">' 
		if (this.has_block('calendar_month_navigation')){
			o +=  '<span style="float: right">'
			o += '<a href="#" onclick="Kato.Active.' + this.id + '.year_back()">&laquo;</a> '
			o += '<a href="#" onclick="Kato.Active.' + this.id + '.month_back()">&lt;</a> '
			o += '<a href="#" onclick="Kato.Active.' + this.id + '.month_forward()">&gt;</a> '
			o += '<a href="#" onclick="Kato.Active.' + this.id + '.year_forward()">&raquo;</a>'
			o += '</span>'	
		}
		o += date.getFullYear() + ' ' + Kato.Months[date.getMonth()] + '</th></tr>\n'
		o += '<tr><th style="width: 14%">'+Kato.Weekdays[0]+'</th><th style="width: 14%">'+Kato.Weekdays[1]
		o += '</th><th style="width: 14%">'+Kato.Weekdays[2]+'</th><th style="width: 14%">'+Kato.Weekdays[3]
		o += '</th><th style="width: 14%">'+Kato.Weekdays[4]+'</th><th style="width: 14%">'+Kato.Weekdays[5]
		o += '</th><th style="width: 14%">'+Kato.Weekdays[6]+'</th><tr>\n'

		var padding = date.getDay()-1
		if (padding<0)padding += 7
		while (padding--){
			o += '<td>&nbsp;</td>'
		}
		return o
	},
	
	pad_to_end:function(date){
		var o = ''
		var padding = 8-date.getDay()
		if(padding>7)padding -= 7
		while(padding--){
			o += '<td>&nbsp;</td>'
		}
		o += '</tr>'
		return o
	},

	render_week_end:function(date){
		var o = ''
		o += '</tr><tr>'
		return o
	},
	
	after_fetch_data:function(){
		if(this.timestamp_column_index===undefined){
			var timestamp_column_index = this.timestamp_column_index?this.timestamp_column_index:0
			if(!this.timestamp_column_index){
				for(var v = 0; v < this.columns.length; v++){
					if(this.columns[v].type.indexOf('timestamp')===0 || this.columns[v].type.indexOf('date')===0){
						this.timestamp_column = this.columns[v].column
						this.timestamp_column_index = v
						timestamp_column_index = v
						this.timestamp_column_index = v
					}
				}
			}
		}
		
		this.calendar_hash = {}
		var min_date = '9'
		var max_date = '0'
		for (var v=0; v < this.rows.length; v++){
			if(!this.rows[v][this.timestamp_column_index]) continue;
			var date = (this.rows[v][this.timestamp_column_index].substr(0,10))
			if(date>max_date) max_date = date
			if(date<min_date) min_date = date
			if(!this.calendar_hash[date]){
				this.calendar_hash[date] = [this.rows[v]]
			} else {
				this.calendar_hash[date].push(this.rows[v])				
			}
		}
		this.min_date = Kato.Utils.date_from_string(min_date)
		this.max_date = Kato.Utils.date_from_string(max_date)
	},
	
	change_date:function(date_string){
		for (var v=0; v < this.onchange_date_handlers.length; v++){
			this.onchange_date_handlers[v].bind(this)(date_string)
		}
	},

	get_day_element:function(date_string){
		return this.id + date_string;
	},
	
	render_day:function(date){
		var o = ''
		date = this.string_from_date(date)
		var events = this.calendar_hash[date]
		if(!events) events = []
		o += '<td><div class="date">'
		if(this.onchange_date_handlers.length>0){
			o += ('<a href="#" onclick="Kato.Active.' + this.id + '.change_date(\'' + date.substr(0,10) + '\'); return false">')
			o += date.substr(8,2)
			o += '</a>'
		} else {
			o += date.substr(8,2)			
		}
		o += '</div><div id="' + this.get_day_element(date) + '"><ul >'
		for(var v=0; v < events.length; v++){
			o += this.render_event(events[v])
		}
		o += '</ul></div></td>'
		return o
	},
	
	render_event:function(event){
		return '<li><a href="#" onclick="Kato.Active.' + this.id+ '.row_clicked(\'' + event[0] + '\'); return false">' + 
		this.cell_renderers[this.column_displayed_index].bind(this)(event,this.column_displayed_index) + '</a></li>'
	},
	
	string_from_date:function(date) {
		var month = '' + (date.getMonth()+1)
		if(month.length==1) month = '0' + month
		var day = '' + (date.getDate())
		if(day.length==1) day = '0' + day		
		return date.getFullYear() + '-' + (month) + '-' + (day)
	},
	
	build_cell:function(row,index){
		return row[index]
	}
})


Kato.Init = function(options){
	Kato.Utils.idle.busy()
	if (options === undefined) options = {}
	Object.extend(Kato.Config, options)
	var url = Kato.Config.BaseUrl + '/php/?view=KatoPrimaryKeys';
	var request = new Ajax.Request(url,{asynchronous:false})
	Kato.PrimaryKeys = eval ('(' + request.transport.responseText + ')')
	Object.extend(Kato.PrimaryKeys,Kato.CustomPrimaryKeys)

	var qs = window.location.search.slice(1)
	if (qs != '') {
		var chunks = qs.split('&')
		for (var i = 0; i < chunks.length; i++) {
			var parts = chunks[i].split('=')
			key = parts[0]
			val = parts[1] ? parts[1] : ''
			if (key.search(/\[\]$/) == -1) {
				Kato.Request[key] = decodeURIComponent(val)
			}
			else {
				realkey = key.replace(/\[\]$/, '')
				if (Kato.Request[realkey] === undefined) {
					Kato.Request[realkey] = []
				}
				Kato.Request[realkey].push(decodeURIComponent(val))
			}
		}
	}

	Kato.Pathname = window.location.pathname
	
	var divs_real = document.body.getElementsByTagName('div');
	var divs = []
	for (var f=0; f < divs_real.length; f++){
		divs.push(divs_real[f])
	}
	for(var f=0; f < divs.length; f++){
		if(!$(divs[f]).hasAttribute('kato_type'))continue;
		Kato.Create(divs[f])
		Kato.Active[divs[f].id].render()
	}
	if(Kato.CustomInit)Kato.CustomInit()
	Kato.Updater()
	Kato.Utils.idle.idle()
}

Kato.BuildFromScratch = function(params,parent){
	var element = document.createElement('div')
	element.setAttribute('id',params['id'])

	if(Kato.Templates[element.id]){
		for (var key in Kato.Templates[element.id]){
			if(key!='id'){
				element.setAttribute('kato_' + key, Kato.Templates[element.id][key])
			} 
		}
	}

	for (var key in params){
		if(key!='id'){
			element.setAttribute('kato_' + key, params[key])
		} 
	}
	parent.innerHTML = ''
	parent.appendChild(element)
	if(Kato.Active[params.id]){			// widget already exists
		Kato.Active[params.id].element = $(element)
	} else {
		Kato.Create(element)				// build widget
	}
}

Kato.Create = function(element){
	element = $(element);
	if (!element.hasAttribute('kato_type')) return;
	var init = eval(element.getAttribute('kato_type'))
	var construct=new init(element)
	if(Kato.Active[element.id]){
		Kato.Error('[error] id '+construct.id+' already taken')
		return
	}
	Kato.Active[element.id] = construct
	try {
		Kato.Build(construct)
	} catch (e){
		Kato.Error(e)
	}
}

Kato.Build = function(construct){
	construct.build()
}

Kato.Debugger = Class.create()
Kato.Debugger.prototype = Object.extend(Kato.Debugger.prototype,{
	
	build:function(){
		this.object_debugged = '---'
		this.render()
	},
	
	render:function(){
		var o = ''
		o += '<input type="button" class="kato_button" value="refresh" onclick="Kato.Active.' + this.id + '.render()"/>'
		
		o += '<select onchange="Kato.Active.' + this.id + '.debug(this.options[this.selectedIndex].value)">\n'
		for (el in Kato.Active){
			if (el==this.object_debugged){
				o += '<option selected="selected">'
			} else {
				o += '<option>'
			}
			o += el + '</option>\n'
		}
		o += '</select> | '
		o += this.object_debugged
		o += '<hr />'
		o += this.tools()		
		this.element.innerHTML = o
	},
	
	tools:function(){
		var o = ''
		o += "<input type='button' class='kato_button' value='view data' onclick='Kato.Debugger.DumpRows(Kato.Active." + this.object_debugged + ".rows)' />"
		o += "<input type='button' class='kato_button' value='model data' onclick='Kato.Debugger.DumpRows(Kato.Active." + this.object_debugged + ".data.rows)' />"
		o += "<input type='button' class='kato_button' value='params' onclick='DumperAlert(Kato.Active." + this.object_debugged + ".params)' />"
		o += "<input type='button' class='kato_button' value='renderers' onclick='DumperAlert(Kato.Active." + this.object_debugged + ".cell_renderers)' />"
		return o
	},
	
	debug:function(name){
		this.object_debugged = name
		this.render()
	}
})

Kato.Debugger.DumpRows = function (rows){
	var o = ''
	for (var v = 0 ; v < rows.length; v++){
		o += rows[v] + '\n'
	}
	alert(o)
}

Kato.Utils.nl2br = function (text){
	if(!text) return ''
	var lines = text.split("\n")

        text = ''

	for (var v=0; v < lines.length; v++){
		line = lines[v]
		line = line.replace(/(.*)/, '<p>$1</p>')
		text += line
	}

        return text;

}


Kato.Utils.textile = function (text){
	if(!text) return ''
	text = text.replace(/\*(.+?)\*/g,'<strong>$1</strong>')
	text = text.replace(/\b_(.+?)_\b/g,'<em>$1</em>')
	text = text.replace(/\b__(.+?)__\b/g,'<i>$1</i>')
	text = text.replace(/\b\*\*(.+?)\*\*\b/g,'<b>$1</b>')
	text = text.replace(/"(.+?)":(https?:\/\/[\w./?&;]*)/g,'<a href="$2">$1</a>')
	
	var lines = text.split("\n")
	text = ''
	for (var v=0; v < lines.length; v++){
		line = lines[v]
		line = line.replace(/^\s*p\.\s+(.*)/, '<p>$1</p>')
		line = line.replace(/^\s*h(\d)\.\s+(.*)/, '<h$1>$2</h$1>')
		text += line
	}
	return text
}

Kato.Utils.escape_quotes = function(string) {
	return string.replace(/"/g, "&quot;")
}


/*
Local variables:
show-trailing-whitespace: nil
End:
*/
